package com.xaaef.grpc.client.util;

import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import lombok.extern.slf4j.Slf4j;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;


@Slf4j
public class GrpcBreaker {

    public static <T> GrpcResponseWrapper<T> fallbackWithValue(Supplier<T> fun, T fallbackValue) {
        return fallback(fun, (e1, e2) -> fallbackValue);
    }


    public static <T> GrpcResponseWrapper<T> fallbackWithException(Supplier<T> fun, Function<StatusRuntimeException, T> fallback) {
        return fallback(fun, (e1, e2) -> fallback.apply(e1));
    }


    public static <T> GrpcResponseWrapper<T> fallbackWithStatus(Supplier<T> fun, Function<Status, T> fallback) {
        return fallback(fun, (e1, e2) -> fallback.apply(e2));
    }


    public static <T> GrpcResponseWrapper<T> fallback(Supplier<T> fun) {
        return fallback(fun, null);
    }


    public static <T> GrpcResponseWrapper<T> fallback(Supplier<T> fun, BiFunction<StatusRuntimeException, Status, T> fallback) {
        try {
            final var value = fun.get();
            return GrpcResponseWrapper.result(Status.OK, value);
        } catch (StatusRuntimeException ex) {
            var errorStatus = ex.getStatus() == Status.UNKNOWN ?
                    Status.UNKNOWN.withCause(ex.getCause()).withDescription(ex.getMessage())
                    : ex.getStatus();
            if (fallback == null) {
                return GrpcResponseWrapper.result(errorStatus, null);
            } else {
                return GrpcResponseWrapper.result(errorStatus, fallback.apply(ex, errorStatus));
            }
        }
    }


}
